/*-------------------------------------------------------------------------
 *
 * fmgr.c
 *	  Postgres 函数管理器。
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *	  src/backend/utils/fmgr/fmgr.c
 *
 *-------------------------------------------------------------------------
 */

#include "postgres.h"

#include "access/detoast.h"
#include "catalog/pg_language.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_type.h"
#include "executor/functions.h"
#include "lib/stringinfo.h"
#include "miscadmin.h"
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
#include "pgstat.h"
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/fmgrtab.h"
#include "utils/guc.h"
#include "utils/lsyscache.h"
#include "utils/syscache.h"

/*
 * 函数调用的钩子
 */
PGDLLIMPORT needs_fmgr_hook_type needs_fmgr_hook = NULL;
PGDLLIMPORT fmgr_hook_type fmgr_hook = NULL;

/*
 * 用于快速查找外部C函数的哈希表
 */
typedef struct
{
	/* fn_oid是哈希键，因此必须位于第一位！ */
	Oid			fn_oid;			/* 外部C函数的OID */
	TransactionId fn_xmin;		/* 用于检查最新状态 */
	ItemPointerData fn_tid;
	PGFunction	user_fn;		/* 函数的地址 */
	const Pg_finfo_record *inforec; /* 它的记录地址 */
} CFuncHashTabEntry;

static HTAB *CFuncHash = NULL;


static void fc_fmgr_info_cxt_security(Oid fc_functionId, FmgrInfo *fc_finfo, MemoryContext fc_mcxt,
								   bool fc_ignore_security);
static void fc_fmgr_info_C_lang(Oid fc_functionId, FmgrInfo *fc_finfo, HeapTuple fc_procedureTuple);
static void fc_fmgr_info_other_lang(Oid fc_functionId, FmgrInfo *fc_finfo, HeapTuple fc_procedureTuple);
static CFuncHashTabEntry *fc_lookup_C_func(HeapTuple fc_procedureTuple);
static void fc_record_C_func(HeapTuple fc_procedureTuple,
						  PGFunction fc_user_fn, const Pg_finfo_record *fc_inforec);

/* extern，以便通过JIT调用 */
extern Datum fmgr_security_definer(PG_FUNCTION_ARGS);


/*
 * 内置函数表的查找例程。我们可以通过OID
 * 或名称进行搜索，但通过OID搜索速度更快。
 */

static const FmgrBuiltin *
fc_fmgr_isbuiltin(Oid fc_id)
{
	uint16		fc_index;

	/* 仅在原始oid仍被分配时才能快速查找 */
	if (fc_id > fmgr_last_builtin_oid)
		return NULL;

	/*
	 * 查找函数数据。如果在该范围内未命中，
	 * 则很可能是不存在的函数，此处返回NULL将在后续
	 * 触发错误。
	 */
	fc_index = fmgr_builtin_oid_index[fc_id];
	if (fc_index == InvalidOidBuiltinMapping)
		return NULL;

	return &fmgr_builtins[fc_index];
}

/*
 * 通过名称查找内置函数。请注意，数组中可以有多个
 * 相同名称的条目，但它们应都指向相同的例程。
 */
static const FmgrBuiltin *
fc_fmgr_lookupByName(const char *fc_name)
{
	int			fc_i;

	for (fc_i = 0; fc_i < fmgr_nbuiltins; fc_i++)
	{
		if (strcmp(fc_name, fmgr_builtins[fc_i].funcName) == 0)
			return fmgr_builtins + fc_i;
	}
	return NULL;
}

/*
 * 此例程填充一个FmgrInfo结构，给定
 * 要调用的函数的OID。
 *
 * 调用者的CurrentMemoryContext用作info
 * 结构的fn_mcxt；这意味着与info结构附加的任何
 * 附属数据（无论是通过fmgr_info本身还是随后
 * 由函数调用处理程序）都将分配在该上下文中。
 * 调用者必须确保此上下文至少与info结构本身
 * 一样持久。这在info结构位于栈上或新分配的
 * 空间中的典型情况下不是问题。然而，如果一个意图
 * 将info结构存储在一个长期存在的表中，最好使用
 * fmgr_info_cxt。
 */
void fmgr_info(Oid fc_functionId, FmgrInfo *fc_finfo)
{
	fc_fmgr_info_cxt_security(fc_functionId, fc_finfo, CurrentMemoryContext, false);
}

/*
 * 填充一个FmgrInfo结构，指定一个内存上下文， 
 * 以便其附属数据应放置在其中。
 */
void fmgr_info_cxt(Oid fc_functionId, FmgrInfo *fc_finfo, MemoryContext fc_mcxt)
{
	fc_fmgr_info_cxt_security(fc_functionId, fc_finfo, fc_mcxt, false);
}

/*
 * 这个函数执行实际的工作。 ignore_security 通常是 false
 * 但在我们需要避免递归时设置为 true。
 */
static void fc_fmgr_info_cxt_security(Oid fc_functionId, FmgrInfo *fc_finfo, MemoryContext fc_mcxt,
					   bool fc_ignore_security)
{
	const FmgrBuiltin *fc_fbp;
	HeapTuple	fc_procedureTuple;
	Form_pg_proc fc_procedureStruct;
	Datum		fc_prosrcdatum;
	bool		fc_isnull;
	char	   *fc_prosrc;

	/*
	 * fn_oid *必须* 最后填写。有些代码假设如果 fn_oid 有效，
	 * 整个结构就是有效的。有些 FmgrInfo 结构可以存活
	 * elogs。
	 */
	fc_finfo->fn_oid = InvalidOid;
	fc_finfo->fn_extra = NULL;
	fc_finfo->fn_mcxt = fc_mcxt;
	fc_finfo->fn_expr = NULL;		/* 调用者可以稍后设置这个 */

	if ((fc_fbp = fc_fmgr_isbuiltin(fc_functionId)) != NULL)
	{
		/*
		 * 内置函数的快速路径：不用查咨询 pg_proc
		 */
		fc_finfo->fn_nargs = fc_fbp->nargs;
		fc_finfo->fn_strict = fc_fbp->strict;
		fc_finfo->fn_retset = fc_fbp->retset;
		fc_finfo->fn_stats = TRACK_FUNC_ALL;	/* 即，永远不跟踪 */
		fc_finfo->fn_addr = fc_fbp->func;
		fc_finfo->fn_oid = fc_functionId;
		return;
	}

	/* 否则我们需要 pg_proc 条目 */
	fc_procedureTuple = SearchSysCache1(PROCOID, ObjectIdGetDatum(fc_functionId));
	if (!HeapTupleIsValid(fc_procedureTuple))
		elog(ERROR, "cache lookup failed for function %u", fc_functionId);
	fc_procedureStruct = (Form_pg_proc) GETSTRUCT(fc_procedureTuple);

	fc_finfo->fn_nargs = fc_procedureStruct->pronargs;
	fc_finfo->fn_strict = fc_procedureStruct->proisstrict;
	fc_finfo->fn_retset = fc_procedureStruct->proretset;

	/*
	 * 如果它设置了 prosecdef，非空的 proconfig，或者如果插件想要
	 * hook 函数 entry/exit，使用 fmgr_security_definer 调用处理程序 ---
	 * 除非我们再次被 fmgr_security_definer 或
	 * fmgr_info_other_lang 调用。
	 *
	 * 使用 fmgr_security_definer 时，函数统计跟踪始终在外层禁用，
	 * 相反，我们在 fmgr_security_definer 的私有 flinfo 中正确设置标志，
	 * 并在 fmgr_security_definer 内部实施跟踪。这失去了将
	 * fmgr_security_definer 的开销计入函数的能力，但获得了
	 * 将 track_functions GUC 设置为一个有趣函数的本地 GUC 参数并
	 * 实现正确行为的能力。
	 */
	if (!fc_ignore_security &&
		(fc_procedureStruct->prosecdef ||
		 !heap_attisnull(fc_procedureTuple, Anum_pg_proc_proconfig, NULL) ||
		 FmgrHookIsNeeded(fc_functionId)))
	{
		fc_finfo->fn_addr = fmgr_security_definer;
		fc_finfo->fn_stats = TRACK_FUNC_ALL;	/* 即，永远不跟踪 */
		fc_finfo->fn_oid = fc_functionId;
		ReleaseSysCache(fc_procedureTuple);
		return;
	}

	switch (fc_procedureStruct->prolang)
	{
		case INTERNALlanguageId:

			/*
			 * 对于一个普通的内置函数，我们永远不应该到这里
			 * 因为上面的 fmgr_isbuiltin() 搜索应该是成功的。
			 * 然而，如果用户执行了 CREATE FUNCTION 来创建一个
			 * 内置函数的别名，我们可能会到这里。在这种情况下
			 * 我们必须通过名称查找函数。内部函数的名称存储
			 * 在 prosrc 中（它不必与别名的名称相同！）
			 */
			fc_prosrcdatum = SysCacheGetAttr(PROCOID, fc_procedureTuple,
										  Anum_pg_proc_prosrc, &fc_isnull);
			if (fc_isnull)
				elog(ERROR, "null prosrc");
			fc_prosrc = TextDatumGetCString(fc_prosrcdatum);
			fc_fbp = fc_fmgr_lookupByName(fc_prosrc);
			if (fc_fbp == NULL)
#ifdef FDD//cppcheck
			{
#endif
				ereport(ERROR,
						(errcode(ERRCODE_UNDEFINED_FUNCTION),
						 errmsg("internal function \"%s\" is not in internal lookup table",
								fc_prosrc)));
#ifdef FDD//cppcheck
				return;
			}
#endif
			pfree(fc_prosrc);
			/* 我们应该检查 nargs、strict、retset 是否与表匹配吗？ */
			fc_finfo->fn_addr = fc_fbp->func;
			/* 注意这个策略在上面的快速路径中也被假设 */
			fc_finfo->fn_stats = TRACK_FUNC_ALL;	/* 即，永远不跟踪 */
			break;

		case ClanguageId:
			fc_fmgr_info_C_lang(fc_functionId, fc_finfo, fc_procedureTuple);
			fc_finfo->fn_stats = TRACK_FUNC_PL;	/* 即，如果全部跟踪 */
			break;

		case SQLlanguageId:
			fc_finfo->fn_addr = fmgr_sql;
			fc_finfo->fn_stats = TRACK_FUNC_PL;	/* 即，如果全部跟踪 */
			break;

		default:
			fc_fmgr_info_other_lang(fc_functionId, fc_finfo, fc_procedureTuple);
			fc_finfo->fn_stats = TRACK_FUNC_OFF;	/* 即，如果不关闭跟踪 */
			break;
	}

	fc_finfo->fn_oid = fc_functionId;
	ReleaseSysCache(fc_procedureTuple);
}

/*
 * 返回提供 functionId 实现的模块和 C 函数名称。
 *
 * 如果 *mod == NULL 且 *fn == NULL，表示没有已知的 C 符号来实现
 * 函数。
 *
 * 如果 *mod == NULL 且 *fn != NULL，函数由主二进制文件中的符号实现。
 *
 * 如果 *mod != NULL 且 *fn != NULL，函数在扩展共享对象中实现。
 *
 * 返回的模块和函数名称会被 pstrdup 复制到当前
 * 内存上下文中。
 */
void fmgr_symbol(Oid fc_functionId, char **fc_mod, char **fc_fn)
{
	HeapTuple	fc_procedureTuple;
	Form_pg_proc fc_procedureStruct;
	bool		fc_isnull;
	Datum		fc_prosrcattr;
	Datum		fc_probinattr;

	fc_procedureTuple = SearchSysCache1(PROCOID, ObjectIdGetDatum(fc_functionId));
	if (!HeapTupleIsValid(fc_procedureTuple))
		elog(ERROR, "cache lookup failed for function %u", fc_functionId);
	fc_procedureStruct = (Form_pg_proc) GETSTRUCT(fc_procedureTuple);

	if (fc_procedureStruct->prosecdef ||
		!heap_attisnull(fc_procedureTuple, Anum_pg_proc_proconfig, NULL) ||
		FmgrHookIsNeeded(fc_functionId))
	{
		*fc_mod = NULL;			/* 核心二进制文件 */
		*fc_fn = pstrdup("fmgr_security_definer");
		ReleaseSysCache(fc_procedureTuple);
		return;
	}

	/* 请参见 fmgr_info_cxt_security 以获取个别情况 */
	switch (fc_procedureStruct->prolang)
	{
		case INTERNALlanguageId:
			fc_prosrcattr = SysCacheGetAttr(PROCOID, fc_procedureTuple,
										 Anum_pg_proc_prosrc, &fc_isnull);
			if (fc_isnull)
				elog(ERROR, "null prosrc");

			*fc_mod = NULL;		/* 核心二进制文件 */
			*fc_fn = TextDatumGetCString(fc_prosrcattr);
			break;

		case ClanguageId:
			fc_prosrcattr = SysCacheGetAttr(PROCOID, fc_procedureTuple,
										 Anum_pg_proc_prosrc, &fc_isnull);
			if (fc_isnull)
				elog(ERROR, "null prosrc for C function %u", fc_functionId);

			fc_probinattr = SysCacheGetAttr(PROCOID, fc_procedureTuple,
										 Anum_pg_proc_probin, &fc_isnull);
			if (fc_isnull)
				elog(ERROR, "null probin for C function %u", fc_functionId);

			/*
			 * 不需要在这里检查符号存在 / API 版本，已经在
			 * fmgr_info_cxt_security 中检查过。
			 */
			*fc_mod = TextDatumGetCString(fc_probinattr);
			*fc_fn = TextDatumGetCString(fc_prosrcattr);
			break;

		case SQLlanguageId:
			*fc_mod = NULL;		/* 核心二进制文件 */
			*fc_fn = pstrdup("fmgr_sql");
			break;

		default:
			*fc_mod = NULL;
			*fc_fn = NULL;			/* 未知，传递指针 */
			break;
	}

	ReleaseSysCache(fc_procedureTuple);
}


/*
 * C 语言函数的特殊 fmgr_info 处理。注意
 * finfo->fn_oid 还无效。
 */
static void fc_fmgr_info_C_lang(Oid fc_functionId, FmgrInfo *fc_finfo, HeapTuple fc_procedureTuple)
{
	CFuncHashTabEntry *fc_hashentry;
	PGFunction	fc_user_fn;
	const Pg_finfo_record *fc_inforec;
	bool		fc_isnull;

	/*
	 * 查看我们是否已经缓存了函数地址
	 */
	fc_hashentry = fc_lookup_C_func(fc_procedureTuple);
	if (fc_hashentry)
	{
		fc_user_fn = fc_hashentry->user_fn;
		fc_inforec = fc_hashentry->inforec;
	}
	else
	{
		Datum		fc_prosrcattr,
					fc_probinattr;
		char	   *fc_prosrcstring,
				   *fc_probinstring;
		void	   *fc_libraryhandle;

		/*
		 * 获取 prosrc 和 probin 字符串（链接符号和库文件名）。
		 * 虽然一般来说这些列可能为 null，但对于 C 语言函数来说
		 * 是不允许的。
		 */
		fc_prosrcattr = SysCacheGetAttr(PROCOID, fc_procedureTuple,
									 Anum_pg_proc_prosrc, &fc_isnull);
		if (fc_isnull)
			elog(ERROR, "null prosrc for C function %u", fc_functionId);
		fc_prosrcstring = TextDatumGetCString(fc_prosrcattr);

		fc_probinattr = SysCacheGetAttr(PROCOID, fc_procedureTuple,
									 Anum_pg_proc_probin, &fc_isnull);
		if (fc_isnull)
			elog(ERROR, "null probin for C function %u", fc_functionId);
		fc_probinstring = TextDatumGetCString(fc_probinattr);

		/* 查找函数本身 */
		fc_user_fn = load_external_function(fc_probinstring, fc_prosrcstring, true,
										 &fc_libraryhandle);

		/* 获取函数信息记录（真实或默认） */
		fc_inforec = fetch_finfo_record(fc_libraryhandle, fc_prosrcstring);

		/* 缓存地址以供后续调用 */
		fc_record_C_func(fc_procedureTuple, fc_user_fn, fc_inforec);

		pfree(fc_prosrcstring);
		pfree(fc_probinstring);
	}

	switch (fc_inforec->api_version)
	{
		case 1:
			/* 新风格：直接调用 */
			fc_finfo->fn_addr = fc_user_fn;
			break;
		default:
			/* 如果 fetch_finfo_record 完成了它的工作，就不应该到这里 */
			elog(ERROR, "unrecognized function API version: %d",
				 fc_inforec->api_version);
			break;
	}
}

/*
 * 其他语言函数的特殊 fmgr_info 处理。注意
 * finfo->fn_oid 还无效。
 */
static void fc_fmgr_info_other_lang(Oid fc_functionId, FmgrInfo *fc_finfo, HeapTuple fc_procedureTuple)
{
	Form_pg_proc fc_procedureStruct = (Form_pg_proc) GETSTRUCT(fc_procedureTuple);
	Oid			fc_language = fc_procedureStruct->prolang;
	HeapTuple	fc_languageTuple;
	Form_pg_language fc_languageStruct;
	FmgrInfo	fc_plfinfo;

	fc_languageTuple = SearchSysCache1(LANGOID, ObjectIdGetDatum(fc_language));
	if (!HeapTupleIsValid(fc_languageTuple))
		elog(ERROR, "cache lookup failed for language %u", fc_language);
	fc_languageStruct = (Form_pg_language) GETSTRUCT(fc_languageTuple);

	/*
	 * 查找语言的调用处理程序函数，忽略任何
	 * 通常会导致插入 fmgr_security_definer 的属性。
	 * 我们需要返回指向实际 C 语言函数的裸指针。
	 */
	fc_fmgr_info_cxt_security(fc_languageStruct->lanplcallfoid, &fc_plfinfo,
						   CurrentMemoryContext, true);
	fc_finfo->fn_addr = fc_plfinfo.fn_addr;

	ReleaseSysCache(fc_languageTuple);
}

/*
 * 获取并验证给定外部函数的信息记录。
 * 函数由包含库的句柄指定
 * （从 load_external_function 获取）以及函数名。
 *
 * 如果没有信息函数存在于给定名称下，则会引发错误。
 *
 * 这个函数从 fmgr_info_C_lang 中分离出来，以便 fmgr_c_validator
 * 可以验证尚未进入 pg_proc 的函数的信息记录。
 */
const Pg_finfo_record *
fetch_finfo_record(void *fc_filehandle, const char *fc_funcname)
{
	char	   *fc_infofuncname;
	PGFInfoFunction fc_infofunc;
	const Pg_finfo_record *fc_inforec;

	fc_infofuncname = psprintf("pg_finfo_%s", fc_funcname);

	/* 尝试查找信息函数 */
	fc_infofunc = (PGFInfoFunction) lookup_external_function(fc_filehandle,
														  fc_infofuncname);
	if (fc_infofunc == NULL)
	{
		ereport(ERROR,
				(errcode(ERRCODE_UNDEFINED_FUNCTION),
				 errmsg("could not find function information for function \"%s\"",
						fc_funcname),
				 errhint("SQL-callable functions need an accompanying PG_FUNCTION_INFO_V1(funcname).")));
		return NULL;			/* 使编译器静默 */
	}

	/* 找到了，所以调用它 */
	fc_inforec = (*fc_infofunc) ();

	/* 尽可能地验证结果 */
	if (fc_inforec == NULL)
		elog(ERROR, "null result from info function \"%s\"", fc_infofuncname);
	switch (fc_inforec->api_version)
	{
		case 1:
			/* 好的，没有额外的字段需要验证 */
			break;
		default:
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
					 errmsg("unrecognized API version %d reported by info function \"%s\"",
							fc_inforec->api_version, fc_infofuncname)));
			break;
	}

	pfree(fc_infofuncname);
	return fc_inforec;
}


/* -------------------------------------------------------------------------
 * Routines for caching lookup information for external C functions.
 *
 * dfmgr.c中的例程相对较慢，因此我们尝试避免在每个会话中每个外部函数运行它们超过一次。我们使用一个哈希表，以函数OID作为查找键。
 * -------------------------------------------------------------------------
 */

/*
 * lookup_C_func: 尝试在哈希表中查找C函数
 *
 * 如果条目存在并且是最新的，则返回它；否则返回NULL
 */
static CFuncHashTabEntry *
fc_lookup_C_func(HeapTuple fc_procedureTuple)
{
	Oid			fc_fn_oid = ((Form_pg_proc) GETSTRUCT(fc_procedureTuple))->oid;
	CFuncHashTabEntry *fc_entry;

	if (CFuncHash == NULL)
		return NULL;			/* 还没有表 */
	fc_entry = (CFuncHashTabEntry *)
		hash_search(CFuncHash,
					&fc_fn_oid,
					HASH_FIND,
					NULL);
	if (fc_entry == NULL)
		return NULL;			/* 没有这样的条目 */
	if (fc_entry->fn_xmin == HeapTupleHeaderGetRawXmin(fc_procedureTuple->t_data) &&
		ItemPointerEquals(&fc_entry->fn_tid, &fc_procedureTuple->t_self))
		return fc_entry;			/* 好的 */
	return NULL;				/* 条目已过期 */
}

/*
 * record_C_func: 在哈希表中输入（或更新）有关C函数的信息
 */
static void fc_record_C_func(HeapTuple fc_procedureTuple,
			  PGFunction fc_user_fn, const Pg_finfo_record *fc_inforec)
{
	Oid			fc_fn_oid = ((Form_pg_proc) GETSTRUCT(fc_procedureTuple))->oid;
	CFuncHashTabEntry *fc_entry;
	bool		fc_found;

	/* 如果哈希表尚不存在，则创建它 */
	if (CFuncHash == NULL)
	{
		HASHCTL		fc_hash_ctl;

		fc_hash_ctl.keysize = sizeof(Oid);
		fc_hash_ctl.entrysize = sizeof(CFuncHashTabEntry);
		CFuncHash = hash_create("CFuncHash",
								100,
								&fc_hash_ctl,
								HASH_ELEM | HASH_BLOBS);
	}

	fc_entry = (CFuncHashTabEntry *)
		hash_search(CFuncHash,
					&fc_fn_oid,
					HASH_ENTER,
					&fc_found);
	/* OID已经填写 */
	fc_entry->fn_xmin = HeapTupleHeaderGetRawXmin(fc_procedureTuple->t_data);
	fc_entry->fn_tid = fc_procedureTuple->t_self;
	fc_entry->user_fn = fc_user_fn;
	fc_entry->inforec = fc_inforec;
}


/*
 * 复制FmgrInfo结构
 *
 * 这本质上有点虚假，因为我们无法可靠地复制与语言相关的附属信息。我们通过将fn_extra置为零来作弊，这意味着附属信息必须重新计算。
 */
void fmgr_info_copy(FmgrInfo *fc_dstinfo, FmgrInfo *fc_srcinfo,
			   MemoryContext fc_destcxt)
{
	memcpy(fc_dstinfo, fc_srcinfo, sizeof(FmgrInfo));
	fc_dstinfo->fn_mcxt = fc_destcxt;
	fc_dstinfo->fn_extra = NULL;
}


/*
 * fmgr_internal_validator的专用查找例程：给定内部函数的名称，返回该函数的OID。
 * 如果名称未被识别，则返回InvalidOid。
 */
Oid fmgr_internal_function(const char *fc_proname)
{
	const FmgrBuiltin *fc_fbp = fc_fmgr_lookupByName(fc_proname);

	if (fc_fbp == NULL)
		return InvalidOid;
	return fc_fbp->foid;
}


/*
 * 支持security-definer和proconfig使用的函数。我们使用相同的调用处理程序支持这两种功能，因为它们通常一起使用，涉及两个级别的调用处理程序将是低效的（并且不易于表示）。
 */
struct fmgr_security_definer_cache
{
	FmgrInfo	flinfo;			/* 目标函数的查找信息 */
	Oid			userid;			/* 要设置的用户ID，或InvalidOid */
	ArrayType  *proconfig;		/* 要设置的GUC值，或NULL */
	Datum		arg;			/* 插件模块的透传参数 */
};

/*
 * security-definer/proconfig/plugin-hooked函数的函数处理程序。
 * 我们提取实际函数的OID，并再次进行fmgr查找。
 * 然后我们获取pg_proc行并复制所有者ID和proconfig字段。
 * （所有这些信息在当前查询的持续期间内被缓存。）
 * 为了执行调用，我们暂时用缓存和查找的对象替换flinfo，同时保持外部fcinfo（其中包含所有实际参数等）不变。这不是可重入的，但fcinfo本身无论如何不能以可重入的方式使用。
 */
extern Datum
fmgr_security_definer(PG_FUNCTION_ARGS)
{
	Datum		fc_result;
	struct fmgr_security_definer_cache *volatile fc_fcache;
	FmgrInfo   *fc_save_flinfo;
	Oid			fc_save_userid;
	int			fc_save_sec_context;
	volatile int fc_save_nestlevel;
	PgStat_FunctionCallUsage fc_fcusage;

	if (!fcinfo->flinfo->fn_extra)
	{
		HeapTuple	fc_tuple;
		Form_pg_proc fc_procedureStruct;
		Datum		fc_datum;
		bool		fc_isnull;
		MemoryContext fc_oldcxt;

		fc_fcache = MemoryContextAllocZero(fcinfo->flinfo->fn_mcxt,
										sizeof(*fc_fcache));

		fc_fmgr_info_cxt_security(fcinfo->flinfo->fn_oid, &fc_fcache->flinfo,
							   fcinfo->flinfo->fn_mcxt, true);
		fc_fcache->flinfo.fn_expr = fcinfo->flinfo->fn_expr;

		fc_tuple = SearchSysCache1(PROCOID,
								ObjectIdGetDatum(fcinfo->flinfo->fn_oid));
		if (!HeapTupleIsValid(fc_tuple))
			elog(ERROR, "cache lookup failed for function %u",
				 fcinfo->flinfo->fn_oid);
		fc_procedureStruct = (Form_pg_proc) GETSTRUCT(fc_tuple);

		if (fc_procedureStruct->prosecdef)
			fc_fcache->userid = fc_procedureStruct->proowner;

		fc_datum = SysCacheGetAttr(PROCOID, fc_tuple, Anum_pg_proc_proconfig,
								&fc_isnull);
		if (!fc_isnull)
		{
			fc_oldcxt = MemoryContextSwitchTo(fcinfo->flinfo->fn_mcxt);
			fc_fcache->proconfig = DatumGetArrayTypePCopy(fc_datum);
			MemoryContextSwitchTo(fc_oldcxt);
		}

		ReleaseSysCache(fc_tuple);

		fcinfo->flinfo->fn_extra = fc_fcache;
	}
	else
		fc_fcache = fcinfo->flinfo->fn_extra;

	/* GetUserIdAndSecContext是相对便宜的，因此无妨浪费一次调用 */
	GetUserIdAndSecContext(&fc_save_userid, &fc_save_sec_context);
	if (fc_fcache->proconfig)		/* 需要一个新的 GUC 嵌套级别 */
		fc_save_nestlevel = NewGUCNestLevel();
	else
		fc_save_nestlevel = 0;		/* 保持编译器安静 */

	if (OidIsValid(fc_fcache->userid))
		SetUserIdAndSecContext(fc_fcache->userid,
							   fc_save_sec_context | SECURITY_LOCAL_USERID_CHANGE);

	if (fc_fcache->proconfig)
	{
		ProcessGUCArray(fc_fcache->proconfig,
						(superuser() ? PGC_SUSET : PGC_USERSET),
						PGC_S_SESSION,
						GUC_ACTION_SAVE);
	}

	/* 函数管理器钩子 */
	if (fmgr_hook)
		(*fmgr_hook) (FHET_START, &fc_fcache->flinfo, &fc_fcache->arg);

	/*
 * 我们不需要在错误时恢复GUC或用户ID设置，因为随后的事务或子事务中止会做到这一点。PG_TRY块仅在于清理flinfo链接。
 */
	fc_save_flinfo = fcinfo->flinfo;

	PG_TRY();
	{
		fcinfo->flinfo = &fc_fcache->flinfo;

		/* 请参阅fmgr_info_cxt_security中的说明 */
		pgstat_init_function_usage(fcinfo, &fc_fcusage);

		fc_result = FunctionCallInvoke(fcinfo);

		/*
 * 我们可能正在调用常规或集合返回函数，因此必须测试以查看使用哪个最终标志。
 */
		pgstat_end_function_usage(&fc_fcusage,
								  (fcinfo->resultinfo == NULL ||
								   !IsA(fcinfo->resultinfo, ReturnSetInfo) ||
								   ((ReturnSetInfo *) fcinfo->resultinfo)->isDone != ExprMultipleResult));
	}
	PG_CATCH();
	{
		fcinfo->flinfo = fc_save_flinfo;
		if (fmgr_hook)
			(*fmgr_hook) (FHET_ABORT, &fc_fcache->flinfo, &fc_fcache->arg);
		PG_RE_THROW();
	}
	PG_END_TRY();

	fcinfo->flinfo = fc_save_flinfo;

	if (fc_fcache->proconfig)
		AtEOXact_GUC(true, fc_save_nestlevel);
	if (OidIsValid(fc_fcache->userid))
		SetUserIdAndSecContext(fc_save_userid, fc_save_sec_context);
	if (fmgr_hook)
		(*fmgr_hook) (FHET_END, &fc_fcache->flinfo, &fc_fcache->arg);

	return fc_result;
}


/* -------------------------------------------------------------------------
 * 支持fmgr兼容函数调用者的例程
 * -------------------------------------------------------------------------
 */

/*
 * 这些用于调用具有直接计算参数列表的特定命名函数。请注意，参数和结果都不允许为NULL。此外，函数不能是需要查看FmgrInfo的函数，因为不会有任何。
 */
Datum DirectFunctionCall1Coll(PGFunction fc_func, Oid fc_collation, Datum fc_arg1)
{
	LOCAL_FCINFO(fcinfo, 1);
	Datum		fc_result;

	InitFunctionCallInfoData(*fcinfo, NULL, 1, fc_collation, NULL, NULL);

	fcinfo->args[0].value = fc_arg1;
	fcinfo->args[0].isnull = false;

	fc_result = (*fc_func) (fcinfo);

	/* 检查结果是否为 null，因为调用者显然不期望出现这种情况 */
	if (fcinfo->isnull)
		elog(ERROR, "function %p returned NULL", (void *) fc_func);

	return fc_result;
}

Datum DirectFunctionCall2Coll(PGFunction fc_func, Oid fc_collation, Datum fc_arg1, Datum fc_arg2)
{
	LOCAL_FCINFO(fcinfo, 2);
	Datum		fc_result;

	InitFunctionCallInfoData(*fcinfo, NULL, 2, fc_collation, NULL, NULL);

	fcinfo->args[0].value = fc_arg1;
	fcinfo->args[0].isnull = false;
	fcinfo->args[1].value = fc_arg2;
	fcinfo->args[1].isnull = false;

	fc_result = (*fc_func) (fcinfo);

	/* 检查结果是否为 null，因为调用者显然不期望出现这种情况 */
	if (fcinfo->isnull)
		elog(ERROR, "function %p returned NULL", (void *) fc_func);

	return fc_result;
}

Datum DirectFunctionCall3Coll(PGFunction fc_func, Oid fc_collation, Datum fc_arg1, Datum fc_arg2,
						Datum fc_arg3)
{
	LOCAL_FCINFO(fcinfo, 3);
	Datum		fc_result;

	InitFunctionCallInfoData(*fcinfo, NULL, 3, fc_collation, NULL, NULL);

	fcinfo->args[0].value = fc_arg1;
	fcinfo->args[0].isnull = false;
	fcinfo->args[1].value = fc_arg2;
	fcinfo->args[1].isnull = false;
	fcinfo->args[2].value = fc_arg3;
	fcinfo->args[2].isnull = false;

	fc_result = (*fc_func) (fcinfo);

	/* 检查结果是否为 null，因为调用者显然不期望出现这种情况 */
	if (fcinfo->isnull)
		elog(ERROR, "function %p returned NULL", (void *) fc_func);

	return fc_result;
}

Datum DirectFunctionCall4Coll(PGFunction fc_func, Oid fc_collation, Datum fc_arg1, Datum fc_arg2,
						Datum fc_arg3, Datum fc_arg4)
{
	LOCAL_FCINFO(fcinfo, 4);
	Datum		fc_result;

	InitFunctionCallInfoData(*fcinfo, NULL, 4, fc_collation, NULL, NULL);

	fcinfo->args[0].value = fc_arg1;
	fcinfo->args[0].isnull = false;
	fcinfo->args[1].value = fc_arg2;
	fcinfo->args[1].isnull = false;
	fcinfo->args[2].value = fc_arg3;
	fcinfo->args[2].isnull = false;
	fcinfo->args[3].value = fc_arg4;
	fcinfo->args[3].isnull = false;

	fc_result = (*fc_func) (fcinfo);

	/* 检查结果是否为 null，因为调用者显然不期望出现这种情况 */
	if (fcinfo->isnull)
		elog(ERROR, "function %p returned NULL", (void *) fc_func);

	return fc_result;
}

Datum DirectFunctionCall5Coll(PGFunction fc_func, Oid fc_collation, Datum fc_arg1, Datum fc_arg2,
						Datum fc_arg3, Datum fc_arg4, Datum fc_arg5)
{
	LOCAL_FCINFO(fcinfo, 5);
	Datum		fc_result;

	InitFunctionCallInfoData(*fcinfo, NULL, 5, fc_collation, NULL, NULL);

	fcinfo->args[0].value = fc_arg1;
	fcinfo->args[0].isnull = false;
	fcinfo->args[1].value = fc_arg2;
	fcinfo->args[1].isnull = false;
	fcinfo->args[2].value = fc_arg3;
	fcinfo->args[2].isnull = false;
	fcinfo->args[3].value = fc_arg4;
	fcinfo->args[3].isnull = false;
	fcinfo->args[4].value = fc_arg5;
	fcinfo->args[4].isnull = false;

	fc_result = (*fc_func) (fcinfo);

	/* 检查结果是否为 null，因为调用者显然不期望出现这种情况 */
	if (fcinfo->isnull)
		elog(ERROR, "function %p returned NULL", (void *) fc_func);

	return fc_result;
}

Datum DirectFunctionCall6Coll(PGFunction fc_func, Oid fc_collation, Datum fc_arg1, Datum fc_arg2,
						Datum fc_arg3, Datum fc_arg4, Datum fc_arg5,
						Datum fc_arg6)
{
	LOCAL_FCINFO(fcinfo, 6);
	Datum		fc_result;

	InitFunctionCallInfoData(*fcinfo, NULL, 6, fc_collation, NULL, NULL);

	fcinfo->args[0].value = fc_arg1;
	fcinfo->args[0].isnull = false;
	fcinfo->args[1].value = fc_arg2;
	fcinfo->args[1].isnull = false;
	fcinfo->args[2].value = fc_arg3;
	fcinfo->args[2].isnull = false;
	fcinfo->args[3].value = fc_arg4;
	fcinfo->args[3].isnull = false;
	fcinfo->args[4].value = fc_arg5;
	fcinfo->args[4].isnull = false;
	fcinfo->args[5].value = fc_arg6;
	fcinfo->args[5].isnull = false;

	fc_result = (*fc_func) (fcinfo);

	/* 检查结果是否为 null，因为调用者显然不期望出现这种情况 */
	if (fcinfo->isnull)
		elog(ERROR, "function %p returned NULL", (void *) fc_func);

	return fc_result;
}

Datum DirectFunctionCall7Coll(PGFunction fc_func, Oid fc_collation, Datum fc_arg1, Datum fc_arg2,
						Datum fc_arg3, Datum fc_arg4, Datum fc_arg5,
						Datum fc_arg6, Datum fc_arg7)
{
	LOCAL_FCINFO(fcinfo, 7);
	Datum		fc_result;

	InitFunctionCallInfoData(*fcinfo, NULL, 7, fc_collation, NULL, NULL);

	fcinfo->args[0].value = fc_arg1;
	fcinfo->args[0].isnull = false;
	fcinfo->args[1].value = fc_arg2;
	fcinfo->args[1].isnull = false;
	fcinfo->args[2].value = fc_arg3;
	fcinfo->args[2].isnull = false;
	fcinfo->args[3].value = fc_arg4;
	fcinfo->args[3].isnull = false;
	fcinfo->args[4].value = fc_arg5;
	fcinfo->args[4].isnull = false;
	fcinfo->args[5].value = fc_arg6;
	fcinfo->args[5].isnull = false;
	fcinfo->args[6].value = fc_arg7;
	fcinfo->args[6].isnull = false;

	fc_result = (*fc_func) (fcinfo);

	/* 检查结果是否为 null，因为调用者显然不期望出现这种情况 */
	if (fcinfo->isnull)
		elog(ERROR, "function %p returned NULL", (void *) fc_func);

	return fc_result;
}

Datum DirectFunctionCall8Coll(PGFunction fc_func, Oid fc_collation, Datum fc_arg1, Datum fc_arg2,
						Datum fc_arg3, Datum fc_arg4, Datum fc_arg5,
						Datum fc_arg6, Datum fc_arg7, Datum fc_arg8)
{
	LOCAL_FCINFO(fcinfo, 8);
	Datum		fc_result;

	InitFunctionCallInfoData(*fcinfo, NULL, 8, fc_collation, NULL, NULL);

	fcinfo->args[0].value = fc_arg1;
	fcinfo->args[0].isnull = false;
	fcinfo->args[1].value = fc_arg2;
	fcinfo->args[1].isnull = false;
	fcinfo->args[2].value = fc_arg3;
	fcinfo->args[2].isnull = false;
	fcinfo->args[3].value = fc_arg4;
	fcinfo->args[3].isnull = false;
	fcinfo->args[4].value = fc_arg5;
	fcinfo->args[4].isnull = false;
	fcinfo->args[5].value = fc_arg6;
	fcinfo->args[5].isnull = false;
	fcinfo->args[6].value = fc_arg7;
	fcinfo->args[6].isnull = false;
	fcinfo->args[7].value = fc_arg8;
	fcinfo->args[7].isnull = false;

	fc_result = (*fc_func) (fcinfo);

	/* 检查结果是否为 null，因为调用者显然不期望出现这种情况 */
	if (fcinfo->isnull)
		elog(ERROR, "function %p returned NULL", (void *) fc_func);

	return fc_result;
}

Datum DirectFunctionCall9Coll(PGFunction fc_func, Oid fc_collation, Datum fc_arg1, Datum fc_arg2,
						Datum fc_arg3, Datum fc_arg4, Datum fc_arg5,
						Datum fc_arg6, Datum fc_arg7, Datum fc_arg8,
						Datum fc_arg9)
{
	LOCAL_FCINFO(fcinfo, 9);
	Datum		fc_result;

	InitFunctionCallInfoData(*fcinfo, NULL, 9, fc_collation, NULL, NULL);

	fcinfo->args[0].value = fc_arg1;
	fcinfo->args[0].isnull = false;
	fcinfo->args[1].value = fc_arg2;
	fcinfo->args[1].isnull = false;
	fcinfo->args[2].value = fc_arg3;
	fcinfo->args[2].isnull = false;
	fcinfo->args[3].value = fc_arg4;
	fcinfo->args[3].isnull = false;
	fcinfo->args[4].value = fc_arg5;
	fcinfo->args[4].isnull = false;
	fcinfo->args[5].value = fc_arg6;
	fcinfo->args[5].isnull = false;
	fcinfo->args[6].value = fc_arg7;
	fcinfo->args[6].isnull = false;
	fcinfo->args[7].value = fc_arg8;
	fcinfo->args[7].isnull = false;
	fcinfo->args[8].value = fc_arg9;
	fcinfo->args[8].isnull = false;

	fc_result = (*fc_func) (fcinfo);

	/* 检查结果是否为 null，因为调用者显然不期望出现这种情况 */
	if (fcinfo->isnull)
		elog(ERROR, "function %p returned NULL", (void *) fc_func);

	return fc_result;
}

/*
 * 这些函数的工作方式类似于DirectFunctionCall函数，不同之处在于它们使用flinfo参数来初始化调用的fcinfo。
 * 推荐被调用者仅使用fn_extra和fn_mcxt字段，因为其他字段通常会描述调用函数而不是被调用者。反之，被调用的函数不应该使用fn_extra，除非其使用与被调用者的兼容。
 */

Datum CallerFInfoFunctionCall1(PGFunction fc_func, FmgrInfo *fc_flinfo, Oid fc_collation, Datum fc_arg1)
{
	LOCAL_FCINFO(fcinfo, 1);
	Datum		fc_result;

	InitFunctionCallInfoData(*fcinfo, fc_flinfo, 1, fc_collation, NULL, NULL);

	fcinfo->args[0].value = fc_arg1;
	fcinfo->args[0].isnull = false;

	fc_result = (*fc_func) (fcinfo);

	/* 检查结果是否为 null，因为调用者显然不期望出现这种情况 */
	if (fcinfo->isnull)
		elog(ERROR, "function %p returned NULL", (void *) fc_func);

	return fc_result;
}

Datum CallerFInfoFunctionCall2(PGFunction fc_func, FmgrInfo *fc_flinfo, Oid fc_collation, Datum fc_arg1, Datum fc_arg2)
{
	LOCAL_FCINFO(fcinfo, 2);
	Datum		fc_result;

	InitFunctionCallInfoData(*fcinfo, fc_flinfo, 2, fc_collation, NULL, NULL);

	fcinfo->args[0].value = fc_arg1;
	fcinfo->args[0].isnull = false;
	fcinfo->args[1].value = fc_arg2;
	fcinfo->args[1].isnull = false;

	fc_result = (*fc_func) (fcinfo);

	/* 检查结果是否为 null，因为调用者显然不期望出现这种情况 */
	if (fcinfo->isnull)
		elog(ERROR, "function %p returned NULL", (void *) fc_func);

	return fc_result;
}

/*
 * 这些用于调用先前查找的函数，拥有直接计算的参数列表。注意参数和结果都不允许为NULL。
 */
Datum FunctionCall0Coll(FmgrInfo *fc_flinfo, Oid fc_collation)
{
	LOCAL_FCINFO(fcinfo, 0);
	Datum		fc_result;

	InitFunctionCallInfoData(*fcinfo, fc_flinfo, 0, fc_collation, NULL, NULL);

	fc_result = FunctionCallInvoke(fcinfo);

	/* 检查结果是否为 null，因为调用者显然不期望出现这种情况 */
	if (fcinfo->isnull)
		elog(ERROR, "function %u returned NULL", fc_flinfo->fn_oid);

	return fc_result;
}

Datum FunctionCall1Coll(FmgrInfo *fc_flinfo, Oid fc_collation, Datum fc_arg1)
{
	LOCAL_FCINFO(fcinfo, 1);
	Datum		fc_result;

	InitFunctionCallInfoData(*fcinfo, fc_flinfo, 1, fc_collation, NULL, NULL);

	fcinfo->args[0].value = fc_arg1;
	fcinfo->args[0].isnull = false;

	fc_result = FunctionCallInvoke(fcinfo);

	/* 检查结果是否为 null，因为调用者显然不期望出现这种情况 */
	if (fcinfo->isnull)
		elog(ERROR, "function %u returned NULL", fc_flinfo->fn_oid);

	return fc_result;
}

Datum FunctionCall2Coll(FmgrInfo *fc_flinfo, Oid fc_collation, Datum fc_arg1, Datum fc_arg2)
{
	LOCAL_FCINFO(fcinfo, 2);
	Datum		fc_result;

	InitFunctionCallInfoData(*fcinfo, fc_flinfo, 2, fc_collation, NULL, NULL);

	fcinfo->args[0].value = fc_arg1;
	fcinfo->args[0].isnull = false;
	fcinfo->args[1].value = fc_arg2;
	fcinfo->args[1].isnull = false;

	fc_result = FunctionCallInvoke(fcinfo);

	/* 检查结果是否为 null，因为调用者显然不期望出现这种情况 */
	if (fcinfo->isnull)
		elog(ERROR, "function %u returned NULL", fc_flinfo->fn_oid);

	return fc_result;
}

Datum FunctionCall3Coll(FmgrInfo *fc_flinfo, Oid fc_collation, Datum fc_arg1, Datum fc_arg2,
				  Datum fc_arg3)
{
	LOCAL_FCINFO(fcinfo, 3);
	Datum		fc_result;

	InitFunctionCallInfoData(*fcinfo, fc_flinfo, 3, fc_collation, NULL, NULL);

	fcinfo->args[0].value = fc_arg1;
	fcinfo->args[0].isnull = false;
	fcinfo->args[1].value = fc_arg2;
	fcinfo->args[1].isnull = false;
	fcinfo->args[2].value = fc_arg3;
	fcinfo->args[2].isnull = false;

	fc_result = FunctionCallInvoke(fcinfo);

	/* 检查结果是否为 null，因为调用者显然不期望出现这种情况 */
	if (fcinfo->isnull)
		elog(ERROR, "function %u returned NULL", fc_flinfo->fn_oid);

	return fc_result;
}

Datum FunctionCall4Coll(FmgrInfo *fc_flinfo, Oid fc_collation, Datum fc_arg1, Datum fc_arg2,
				  Datum fc_arg3, Datum fc_arg4)
{
	LOCAL_FCINFO(fcinfo, 4);
	Datum		fc_result;

	InitFunctionCallInfoData(*fcinfo, fc_flinfo, 4, fc_collation, NULL, NULL);

	fcinfo->args[0].value = fc_arg1;
	fcinfo->args[0].isnull = false;
	fcinfo->args[1].value = fc_arg2;
	fcinfo->args[1].isnull = false;
	fcinfo->args[2].value = fc_arg3;
	fcinfo->args[2].isnull = false;
	fcinfo->args[3].value = fc_arg4;
	fcinfo->args[3].isnull = false;

	fc_result = FunctionCallInvoke(fcinfo);

	/* 检查结果是否为 null，因为调用者显然不期望出现这种情况 */
	if (fcinfo->isnull)
		elog(ERROR, "function %u returned NULL", fc_flinfo->fn_oid);

	return fc_result;
}

Datum FunctionCall5Coll(FmgrInfo *fc_flinfo, Oid fc_collation, Datum fc_arg1, Datum fc_arg2,
				  Datum fc_arg3, Datum fc_arg4, Datum fc_arg5)
{
	LOCAL_FCINFO(fcinfo, 5);
	Datum		fc_result;

	InitFunctionCallInfoData(*fcinfo, fc_flinfo, 5, fc_collation, NULL, NULL);

	fcinfo->args[0].value = fc_arg1;
	fcinfo->args[0].isnull = false;
	fcinfo->args[1].value = fc_arg2;
	fcinfo->args[1].isnull = false;
	fcinfo->args[2].value = fc_arg3;
	fcinfo->args[2].isnull = false;
	fcinfo->args[3].value = fc_arg4;
	fcinfo->args[3].isnull = false;
	fcinfo->args[4].value = fc_arg5;
	fcinfo->args[4].isnull = false;

	fc_result = FunctionCallInvoke(fcinfo);

	/* 检查结果是否为 null，因为调用者显然不期望出现这种情况 */
	if (fcinfo->isnull)
		elog(ERROR, "function %u returned NULL", fc_flinfo->fn_oid);

	return fc_result;
}

Datum FunctionCall6Coll(FmgrInfo *fc_flinfo, Oid fc_collation, Datum fc_arg1, Datum fc_arg2,
				  Datum fc_arg3, Datum fc_arg4, Datum fc_arg5,
				  Datum fc_arg6)
{
	LOCAL_FCINFO(fcinfo, 6);
	Datum		fc_result;

	InitFunctionCallInfoData(*fcinfo, fc_flinfo, 6, fc_collation, NULL, NULL);

	fcinfo->args[0].value = fc_arg1;
	fcinfo->args[0].isnull = false;
	fcinfo->args[1].value = fc_arg2;
	fcinfo->args[1].isnull = false;
	fcinfo->args[2].value = fc_arg3;
	fcinfo->args[2].isnull = false;
	fcinfo->args[3].value = fc_arg4;
	fcinfo->args[3].isnull = false;
	fcinfo->args[4].value = fc_arg5;
	fcinfo->args[4].isnull = false;
	fcinfo->args[5].value = fc_arg6;
	fcinfo->args[5].isnull = false;

	fc_result = FunctionCallInvoke(fcinfo);

	/* 检查结果是否为 null，因为调用者显然不期望出现这种情况 */
	if (fcinfo->isnull)
		elog(ERROR, "function %u returned NULL", fc_flinfo->fn_oid);

	return fc_result;
}

Datum FunctionCall7Coll(FmgrInfo *fc_flinfo, Oid fc_collation, Datum fc_arg1, Datum fc_arg2,
				  Datum fc_arg3, Datum fc_arg4, Datum fc_arg5,
				  Datum fc_arg6, Datum fc_arg7)
{
	LOCAL_FCINFO(fcinfo, 7);
	Datum		fc_result;

	InitFunctionCallInfoData(*fcinfo, fc_flinfo, 7, fc_collation, NULL, NULL);

	fcinfo->args[0].value = fc_arg1;
	fcinfo->args[0].isnull = false;
	fcinfo->args[1].value = fc_arg2;
	fcinfo->args[1].isnull = false;
	fcinfo->args[2].value = fc_arg3;
	fcinfo->args[2].isnull = false;
	fcinfo->args[3].value = fc_arg4;
	fcinfo->args[3].isnull = false;
	fcinfo->args[4].value = fc_arg5;
	fcinfo->args[4].isnull = false;
	fcinfo->args[5].value = fc_arg6;
	fcinfo->args[5].isnull = false;
	fcinfo->args[6].value = fc_arg7;
	fcinfo->args[6].isnull = false;

	fc_result = FunctionCallInvoke(fcinfo);

	/* 检查结果是否为 null，因为调用者显然不期望出现这种情况 */
	if (fcinfo->isnull)
		elog(ERROR, "function %u returned NULL", fc_flinfo->fn_oid);

	return fc_result;
}

Datum FunctionCall8Coll(FmgrInfo *fc_flinfo, Oid fc_collation, Datum fc_arg1, Datum fc_arg2,
				  Datum fc_arg3, Datum fc_arg4, Datum fc_arg5,
				  Datum fc_arg6, Datum fc_arg7, Datum fc_arg8)
{
	LOCAL_FCINFO(fcinfo, 8);
	Datum		fc_result;

	InitFunctionCallInfoData(*fcinfo, fc_flinfo, 8, fc_collation, NULL, NULL);

	fcinfo->args[0].value = fc_arg1;
	fcinfo->args[0].isnull = false;
	fcinfo->args[1].value = fc_arg2;
	fcinfo->args[1].isnull = false;
	fcinfo->args[2].value = fc_arg3;
	fcinfo->args[2].isnull = false;
	fcinfo->args[3].value = fc_arg4;
	fcinfo->args[3].isnull = false;
	fcinfo->args[4].value = fc_arg5;
	fcinfo->args[4].isnull = false;
	fcinfo->args[5].value = fc_arg6;
	fcinfo->args[5].isnull = false;
	fcinfo->args[6].value = fc_arg7;
	fcinfo->args[6].isnull = false;
	fcinfo->args[7].value = fc_arg8;
	fcinfo->args[7].isnull = false;

	fc_result = FunctionCallInvoke(fcinfo);

	/* 检查结果是否为 null，因为调用者显然不期望出现这种情况 */
	if (fcinfo->isnull)
		elog(ERROR, "function %u returned NULL", fc_flinfo->fn_oid);

	return fc_result;
}

Datum FunctionCall9Coll(FmgrInfo *fc_flinfo, Oid fc_collation, Datum fc_arg1, Datum fc_arg2,
				  Datum fc_arg3, Datum fc_arg4, Datum fc_arg5,
				  Datum fc_arg6, Datum fc_arg7, Datum fc_arg8,
				  Datum fc_arg9)
{
	LOCAL_FCINFO(fcinfo, 9);
	Datum		fc_result;

	InitFunctionCallInfoData(*fcinfo, fc_flinfo, 9, fc_collation, NULL, NULL);

	fcinfo->args[0].value = fc_arg1;
	fcinfo->args[0].isnull = false;
	fcinfo->args[1].value = fc_arg2;
	fcinfo->args[1].isnull = false;
	fcinfo->args[2].value = fc_arg3;
	fcinfo->args[2].isnull = false;
	fcinfo->args[3].value = fc_arg4;
	fcinfo->args[3].isnull = false;
	fcinfo->args[4].value = fc_arg5;
	fcinfo->args[4].isnull = false;
	fcinfo->args[5].value = fc_arg6;
	fcinfo->args[5].isnull = false;
	fcinfo->args[6].value = fc_arg7;
	fcinfo->args[6].isnull = false;
	fcinfo->args[7].value = fc_arg8;
	fcinfo->args[7].isnull = false;
	fcinfo->args[8].value = fc_arg9;
	fcinfo->args[8].isnull = false;

	fc_result = FunctionCallInvoke(fcinfo);

	/* 检查结果是否为 null，因为调用者显然不期望出现这种情况 */
	if (fcinfo->isnull)
		elog(ERROR, "function %u returned NULL", fc_flinfo->fn_oid);

	return fc_result;
}


/*
 * 这些用于调用按OID标识的函数，拥有直接计算的参数列表。注意参数和结果都不允许为NULL。这实际上是fmgr_info()后面跟随FunctionCallN()。如果同一函数要重复调用，只需进行一次fmgr_info()，然后使用FunctionCallN()。
 */
Datum OidFunctionCall0Coll(Oid fc_functionId, Oid fc_collation)
{
	FmgrInfo	fc_flinfo;

	fmgr_info(fc_functionId, &fc_flinfo);

	return FunctionCall0Coll(&fc_flinfo, fc_collation);
}

Datum OidFunctionCall1Coll(Oid fc_functionId, Oid fc_collation, Datum fc_arg1)
{
	FmgrInfo	fc_flinfo;

	fmgr_info(fc_functionId, &fc_flinfo);

	return FunctionCall1Coll(&fc_flinfo, fc_collation, fc_arg1);
}

Datum OidFunctionCall2Coll(Oid fc_functionId, Oid fc_collation, Datum fc_arg1, Datum fc_arg2)
{
	FmgrInfo	fc_flinfo;

	fmgr_info(fc_functionId, &fc_flinfo);

	return FunctionCall2Coll(&fc_flinfo, fc_collation, fc_arg1, fc_arg2);
}

Datum OidFunctionCall3Coll(Oid fc_functionId, Oid fc_collation, Datum fc_arg1, Datum fc_arg2,
					 Datum fc_arg3)
{
	FmgrInfo	fc_flinfo;

	fmgr_info(fc_functionId, &fc_flinfo);

	return FunctionCall3Coll(&fc_flinfo, fc_collation, fc_arg1, fc_arg2, fc_arg3);
}

Datum OidFunctionCall4Coll(Oid fc_functionId, Oid fc_collation, Datum fc_arg1, Datum fc_arg2,
					 Datum fc_arg3, Datum fc_arg4)
{
	FmgrInfo	fc_flinfo;

	fmgr_info(fc_functionId, &fc_flinfo);

	return FunctionCall4Coll(&fc_flinfo, fc_collation, fc_arg1, fc_arg2, fc_arg3, fc_arg4);
}

Datum OidFunctionCall5Coll(Oid fc_functionId, Oid fc_collation, Datum fc_arg1, Datum fc_arg2,
					 Datum fc_arg3, Datum fc_arg4, Datum fc_arg5)
{
	FmgrInfo	fc_flinfo;

	fmgr_info(fc_functionId, &fc_flinfo);

	return FunctionCall5Coll(&fc_flinfo, fc_collation, fc_arg1, fc_arg2, fc_arg3, fc_arg4, fc_arg5);
}

Datum OidFunctionCall6Coll(Oid fc_functionId, Oid fc_collation, Datum fc_arg1, Datum fc_arg2,
					 Datum fc_arg3, Datum fc_arg4, Datum fc_arg5,
					 Datum fc_arg6)
{
	FmgrInfo	fc_flinfo;

	fmgr_info(fc_functionId, &fc_flinfo);

	return FunctionCall6Coll(&fc_flinfo, fc_collation, fc_arg1, fc_arg2, fc_arg3, fc_arg4, fc_arg5,
							 fc_arg6);
}

Datum OidFunctionCall7Coll(Oid fc_functionId, Oid fc_collation, Datum fc_arg1, Datum fc_arg2,
					 Datum fc_arg3, Datum fc_arg4, Datum fc_arg5,
					 Datum fc_arg6, Datum fc_arg7)
{
	FmgrInfo	fc_flinfo;

	fmgr_info(fc_functionId, &fc_flinfo);

	return FunctionCall7Coll(&fc_flinfo, fc_collation, fc_arg1, fc_arg2, fc_arg3, fc_arg4, fc_arg5,
							 fc_arg6, fc_arg7);
}

Datum OidFunctionCall8Coll(Oid fc_functionId, Oid fc_collation, Datum fc_arg1, Datum fc_arg2,
					 Datum fc_arg3, Datum fc_arg4, Datum fc_arg5,
					 Datum fc_arg6, Datum fc_arg7, Datum fc_arg8)
{
	FmgrInfo	fc_flinfo;

	fmgr_info(fc_functionId, &fc_flinfo);

	return FunctionCall8Coll(&fc_flinfo, fc_collation, fc_arg1, fc_arg2, fc_arg3, fc_arg4, fc_arg5,
							 fc_arg6, fc_arg7, fc_arg8);
}

Datum OidFunctionCall9Coll(Oid fc_functionId, Oid fc_collation, Datum fc_arg1, Datum fc_arg2,
					 Datum fc_arg3, Datum fc_arg4, Datum fc_arg5,
					 Datum fc_arg6, Datum fc_arg7, Datum fc_arg8,
					 Datum fc_arg9)
{
	FmgrInfo	fc_flinfo;

	fmgr_info(fc_functionId, &fc_flinfo);

	return FunctionCall9Coll(&fc_flinfo, fc_collation, fc_arg1, fc_arg2, fc_arg3, fc_arg4, fc_arg5,
							 fc_arg6, fc_arg7, fc_arg8, fc_arg9);
}


/*
 * 方便调用数据类型I/O函数的特殊情况。
 */

/*
 * 调用之前查找的数据类型输入函数。
 *
 * "str"可以为NULL，以表示我们正在读取NULL。在这种情况下，调用者应假定结果为NULL，但如果函数不严格，我们仍将调用输入函数。因此，这与FunctionCall3几乎一样，但又不完全相同。
 */
Datum InputFunctionCall(FmgrInfo *fc_flinfo, char *fc_str, Oid fc_typioparam, int32 fc_typmod)
{
	LOCAL_FCINFO(fcinfo, 3);
	Datum		fc_result;

	if (fc_str == NULL && fc_flinfo->fn_strict)
		return (Datum) 0;		/* 只返回空结果 */

	InitFunctionCallInfoData(*fcinfo, fc_flinfo, 3, InvalidOid, NULL, NULL);

	fcinfo->args[0].value = CStringGetDatum(fc_str);
	fcinfo->args[0].isnull = false;
	fcinfo->args[1].value = ObjectIdGetDatum(fc_typioparam);
	fcinfo->args[1].isnull = false;
	fcinfo->args[2].value = Int32GetDatum(fc_typmod);
	fcinfo->args[2].isnull = false;

	fc_result = FunctionCallInvoke(fcinfo);

	/* 只有在 str 为 NULL 时才应返回 null 结果 */
	if (fc_str == NULL)
	{
		if (!fcinfo->isnull)
			elog(ERROR, "input function %u returned non-NULL",
				 fc_flinfo->fn_oid);
	}
	else
	{
		if (fcinfo->isnull)
			elog(ERROR, "input function %u returned NULL",
				 fc_flinfo->fn_oid);
	}

	return fc_result;
}

/*
 * 调用先前查找的数据类型输出函数。
 *
 * 不要在 NULL 数据项上调用此函数。
 *
 * 这对于 FunctionCall1 目前来说几乎没有其他作用。
 */
char * OutputFunctionCall(FmgrInfo *fc_flinfo, Datum fc_val)
{
	return DatumGetCString(FunctionCall1(fc_flinfo, fc_val));
}

/*
 * 调用先前查找的数据类型二进制输入函数。
 *
 * "buf" 可以是 NULL，以指示我们正在读取一个 NULL。 在这种情况下
 * 调用者应该假设结果是 NULL，但如果不是严格的话我们还是会调用接收
 * 函数。所以这几乎是 FunctionCall3 相似，但又不完全相同。
 */
Datum ReceiveFunctionCall(FmgrInfo *fc_flinfo, StringInfo fc_buf,
					Oid fc_typioparam, int32 fc_typmod)
{
	LOCAL_FCINFO(fcinfo, 3);
	Datum		fc_result;

	if (fc_buf == NULL && fc_flinfo->fn_strict)
		return (Datum) 0;		/* 只返回空结果 */

	InitFunctionCallInfoData(*fcinfo, fc_flinfo, 3, InvalidOid, NULL, NULL);

	fcinfo->args[0].value = PointerGetDatum(fc_buf);
	fcinfo->args[0].isnull = false;
	fcinfo->args[1].value = ObjectIdGetDatum(fc_typioparam);
	fcinfo->args[1].isnull = false;
	fcinfo->args[2].value = Int32GetDatum(fc_typmod);
	fcinfo->args[2].isnull = false;

	fc_result = FunctionCallInvoke(fcinfo);

	/* 仅在 buf 为 NULL 时应获得空结果 */
	if (fc_buf == NULL)
	{
		if (!fcinfo->isnull)
			elog(ERROR, "receive function %u returned non-NULL",
				 fc_flinfo->fn_oid);
	}
	else
	{
		if (fcinfo->isnull)
			elog(ERROR, "receive function %u returned NULL",
				 fc_flinfo->fn_oid);
	}

	return fc_result;
}

/*
 * 调用先前查找的数据类型二进制输出函数。
 *
 * 不要在 NULL 数据项上调用此函数。
 *
 * 这对于 FunctionCall1 来说几乎没有其他作用，但它确实保证了一个非 toast 的结果，
 * 严格来说底层函数并不保证这一点。
 */
bytea * SendFunctionCall(FmgrInfo *fc_flinfo, Datum fc_val)
{
	return DatumGetByteaP(FunctionCall1(fc_flinfo, fc_val));
}

/*
 * 如上所述，适用于由 OID 标识的 I/O 函数。 这些函数仅在很少执行的代码路径中使用。
 * 它们不仅速度慢，而且会泄漏内存。
 */
Datum OidInputFunctionCall(Oid fc_functionId, char *fc_str, Oid fc_typioparam, int32 fc_typmod)
{
	FmgrInfo	fc_flinfo;

	fmgr_info(fc_functionId, &fc_flinfo);
	return InputFunctionCall(&fc_flinfo, fc_str, fc_typioparam, fc_typmod);
}

char * OidOutputFunctionCall(Oid fc_functionId, Datum fc_val)
{
	FmgrInfo	fc_flinfo;

	fmgr_info(fc_functionId, &fc_flinfo);
	return OutputFunctionCall(&fc_flinfo, fc_val);
}

Datum OidReceiveFunctionCall(Oid fc_functionId, StringInfo fc_buf,
					   Oid fc_typioparam, int32 fc_typmod)
{
	FmgrInfo	fc_flinfo;

	fmgr_info(fc_functionId, &fc_flinfo);
	return ReceiveFunctionCall(&fc_flinfo, fc_buf, fc_typioparam, fc_typmod);
}

bytea * OidSendFunctionCall(Oid fc_functionId, Datum fc_val)
{
	FmgrInfo	fc_flinfo;

	fmgr_info(fc_functionId, &fc_flinfo);
	return SendFunctionCall(&fc_flinfo, fc_val);
}


/*-------------------------------------------------------------------------
 *		标准可通过引用传递的数据类型的支持例程
 *
 * 如果 Datum 足够宽，则 int8 和 float8 可以按值传递。
 * （出于向后兼容的原因，我们允许在编译时选择按引用传递，即使按值传递是可能的。）
 *
 * 注意：只有一个开关控制 int8 和 float8 的按值传递选项；
 * 这是为了避免对可能有任一表示法的时间戳类型使事情变得过于复杂。
-------------------------------------------------------------------------
 */

#ifndef USE_FLOAT8_BYVAL		/* 也控制 int8 */

Datum Int64GetDatum(int64 fc_X)
{
	int64	   *fc_retval = (int64 *) palloc(sizeof(int64));

	*fc_retval = fc_X;
	return PointerGetDatum(fc_retval);
}

Datum Float8GetDatum(float8 fc_X)
{
	float8	   *fc_retval = (float8 *) palloc(sizeof(float8));

	*fc_retval = fc_X;
	return PointerGetDatum(fc_retval);
}
#endif							/* USE_FLOAT8_BYVAL */


/*-------------------------------------------------------------------------
 *		可 toast 数据类型的支持例程
 *-------------------------------------------------------------------------
 */

struct varlena * pg_detoast_datum(struct varlena *fc_datum)
{
	if (VARATT_IS_EXTENDED(fc_datum))
		return detoast_attr(fc_datum);
	else
		return fc_datum;
}

struct varlena * pg_detoast_datum_copy(struct varlena *fc_datum)
{
	if (VARATT_IS_EXTENDED(fc_datum))
		return detoast_attr(fc_datum);
	else
	{
		/* 制作 varlena 对象的可修改副本 */
		Size		fc_len = VARSIZE(fc_datum);
		struct varlena *fc_result = (struct varlena *) palloc(fc_len);

		memcpy(fc_result, fc_datum, fc_len);
		return fc_result;
	}
}

struct varlena * pg_detoast_datum_slice(struct varlena *fc_datum, int32 fc_first, int32 fc_count)
{
	/* 仅从 toast 关系中获取指定部分 */
	return detoast_attr_slice(fc_datum, fc_first, fc_count);
}

struct varlena * pg_detoast_datum_packed(struct varlena *fc_datum)
{
	if (VARATT_IS_COMPRESSED(fc_datum) || VARATT_IS_EXTERNAL(fc_datum))
		return detoast_attr(fc_datum);
	else
		return fc_datum;
}

/*-------------------------------------------------------------------------
 *		从 fn_expr 解析树中提取信息的支持例程
 *
 * 这些都是多态函数所需的，这些函数接受多个可能的输入类型，并且需要解析器的帮助来知道它们有什么。
 * 此外，一些函数可能对参数是否为常量感兴趣。
 * 接受 VARIADIC ANY 的函数也需要了解 VARIADIC 关键字。
-------------------------------------------------------------------------
 */

/*
 * 获取函数返回类型的实际类型 OID
 *
 * 如果信息不可用，则返回 InvalidOid
 */
Oid get_fn_expr_rettype(FmgrInfo *fc_flinfo)
{
	Node	   *fc_expr;

	/*
	 * 如果我们没有 FmgrInfo 或其 fn_expr 节点尚未初始化，则无法返回任何有用的信息
	 */
	if (!fc_flinfo || !fc_flinfo->fn_expr)
		return InvalidOid;

	fc_expr = fc_flinfo->fn_expr;

	return exprType(fc_expr);
}

/*
 * 获取特定函数参数的实际类型 OID（从 0 开始计数）
 *
 * 如果信息不可用，则返回 InvalidOid
 */
Oid get_fn_expr_argtype(FmgrInfo *fc_flinfo, int fc_argnum)
{
	/*
	 * 如果我们没有 FmgrInfo 或其 fn_expr 节点尚未初始化，则无法返回任何有用的信息
	 */
	if (!fc_flinfo || !fc_flinfo->fn_expr)
		return InvalidOid;

	return get_call_expr_argtype(fc_flinfo->fn_expr, fc_argnum);
}

/*
 * 获取特定函数参数的实际类型 OID（从 0 开始计数），
 * 但从调用表达式树而不是 FmgrInfo 开始工作
 *
 * 如果信息不可用，则返回 InvalidOid
 */
Oid get_call_expr_argtype(Node *fc_expr, int fc_argnum)
{
	List	   *fc_args;
	Oid			fc_argtype;

	if (fc_expr == NULL)
		return InvalidOid;

	if (IsA(fc_expr, FuncExpr))
		fc_args = ((FuncExpr *) fc_expr)->args;
	else if (IsA(fc_expr, OpExpr))
		fc_args = ((OpExpr *) fc_expr)->args;
	else if (IsA(fc_expr, DistinctExpr))
		fc_args = ((DistinctExpr *) fc_expr)->args;
	else if (IsA(fc_expr, ScalarArrayOpExpr))
		fc_args = ((ScalarArrayOpExpr *) fc_expr)->args;
	else if (IsA(fc_expr, NullIfExpr))
		fc_args = ((NullIfExpr *) fc_expr)->args;
	else if (IsA(fc_expr, WindowFunc))
		fc_args = ((WindowFunc *) fc_expr)->args;
	else
		return InvalidOid;

	if (fc_argnum < 0 || fc_argnum >= list_length(fc_args))
		return InvalidOid;

	fc_argtype = exprType((Node *) list_nth(fc_args, fc_argnum));

	/*
	 * 针对 ScalarArrayOpExpr 的特殊黑客：底层函数实际将接收到的
	 * 是数组的元素类型。
	 */
	if (IsA(fc_expr, ScalarArrayOpExpr) &&
		fc_argnum == 1)
		fc_argtype = get_base_element_type(fc_argtype);

	return fc_argtype;
}

/*
 * 了解特定函数参数在查询期间是否为常量
 *
 * 如果信息不可用，则返回 false
 */
bool get_fn_expr_arg_stable(FmgrInfo *fc_flinfo, int fc_argnum)
{
	/*
	 * 如果我们没有 FmgrInfo 或其 fn_expr 节点尚未初始化，则无法返回任何有用的信息
	 */
	if (!fc_flinfo || !fc_flinfo->fn_expr)
		return false;

	return get_call_expr_arg_stable(fc_flinfo->fn_expr, fc_argnum);
}

/*
 * 了解特定函数参数在查询期间是否为常量，但从调用表达式树开始工作
 *
 * 如果信息不可用，则返回 false
 */
bool get_call_expr_arg_stable(Node *fc_expr, int fc_argnum)
{
	List	   *fc_args;
	Node	   *fc_arg;

	if (fc_expr == NULL)
		return false;

	if (IsA(fc_expr, FuncExpr))
		fc_args = ((FuncExpr *) fc_expr)->args;
	else if (IsA(fc_expr, OpExpr))
		fc_args = ((OpExpr *) fc_expr)->args;
	else if (IsA(fc_expr, DistinctExpr))
		fc_args = ((DistinctExpr *) fc_expr)->args;
	else if (IsA(fc_expr, ScalarArrayOpExpr))
		fc_args = ((ScalarArrayOpExpr *) fc_expr)->args;
	else if (IsA(fc_expr, NullIfExpr))
		fc_args = ((NullIfExpr *) fc_expr)->args;
	else if (IsA(fc_expr, WindowFunc))
		fc_args = ((WindowFunc *) fc_expr)->args;
	else
		return false;

	if (fc_argnum < 0 || fc_argnum >= list_length(fc_args))
		return false;

	fc_arg = (Node *) list_nth(fc_args, fc_argnum);

	/*
	 * 真实的 Const 或外部 Param 将具有在查询执行期间不会变化的值。
	 * 将来我们可能希望考虑其他情况，例如 now()。
	 */
	if (IsA(fc_arg, Const))
		return true;
	if (IsA(fc_arg, Param) &&
		((Param *) fc_arg)->paramkind == PARAM_EXTERN)
		return true;

	return false;
}

/*
 * 从函数调用中获取 VARIADIC 标志
 *
 * 如果信息不可用，则返回 false（默认假设）
 *
 * 注意这通常仅对 VARIADIC ANY 函数感兴趣
 */
bool get_fn_expr_variadic(FmgrInfo *fc_flinfo)
{
	Node	   *fc_expr;

	/*
	 * 如果我们没有 FmgrInfo 或其 fn_expr 节点尚未初始化，则无法返回任何有用的信息
	 */
	if (!fc_flinfo || !fc_flinfo->fn_expr)
		return false;

	fc_expr = fc_flinfo->fn_expr;

	if (IsA(fc_expr, FuncExpr))
		return ((FuncExpr *) fc_expr)->funcvariadic;
	else
		return false;
}

/*
 * 将选项设置为 opclass 支持函数的 FmgrInfo。
 *
 * Opclass 支持函数在表达式之外调用。 由于这个原因，
 * 我们可以使用 fn_expr 将 opclass 选项存储为 bytea 常量。
 */
void set_fn_opclass_options(FmgrInfo *fc_flinfo, bytea *fc_options)
{
	fc_flinfo->fn_expr = (Node *) makeConst(BYTEAOID, -1, InvalidOid, -1,
										 PointerGetDatum(fc_options),
										 fc_options == NULL, false);
}

/*
 * 检查是否为 opclass 支持函数定义了选项。
 */
bool has_fn_opclass_options(FmgrInfo *fc_flinfo)
{
	if (fc_flinfo && fc_flinfo->fn_expr && IsA(fc_flinfo->fn_expr, Const))
	{
		Const	   *fc_expr = (Const *) fc_flinfo->fn_expr;

		if (fc_expr->consttype == BYTEAOID)
			return !fc_expr->constisnull;
	}
	return false;
}

/*
 * 获取 opclass 支持函数的选项。
 */
bytea * get_fn_opclass_options(FmgrInfo *fc_flinfo)
{
	if (fc_flinfo && fc_flinfo->fn_expr && IsA(fc_flinfo->fn_expr, Const))
	{
		Const	   *fc_expr = (Const *) fc_flinfo->fn_expr;

		if (fc_expr->consttype == BYTEAOID)
			return fc_expr->constisnull ? NULL : DatumGetByteaP(fc_expr->constvalue);
	}

	ereport(ERROR,
			(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
			 errmsg("operator class options info is absent in function call context")));

	return NULL;
}

/*-------------------------------------------------------------------------
 *		过程语言实现的支持例程
 *-------------------------------------------------------------------------
 */


/*
 * 验证验证器是否实际上与特定功能的语言相关联，并且用户是否对语言和功能都有访问权限。所有验证器在执行任何实质性操作之前都应该调用此函数。这样做可确保用户不能通过对验证器的显式调用实现他无法通过 CREATE FUNCTION 或仅仅调用现有功能实现的任何操作。
 *
 * 当此函数返回 false 时，调用者应跳过所有验证工作，并调用 PG_RETURN_VOID()。目前这种情况永远不会发生；它保留用于未来扩展。
 *
 * 特别是，检查验证器是否对应于功能的语言允许不受信任的语言验证器假设它们只处理超级用户选择的源代码。（不受信任的语言调用处理程序按照定义是这样假设的。）缺乏 USAGE 语言权限的用户将无法通过 CREATE FUNCTION 访问验证器，因此我们检查这一点以阻止显式调用。此外，检查功能的 EXECUTE 权限通常是多余的，因为大多数用户可以克隆功能以获取可执行副本。对于没有数据库 TEMP 权限和没有永久模式 CREATE 权限的用户，这一点是有意义的，因为他们无法创建任何功能。此外，如果功能通过功能 OID 或名称跟踪持久状态，验证原始功能可能会导致比创建和验证其克隆更多的恶作剧。
 */
bool CheckFunctionValidatorAccess(Oid fc_validatorOid, Oid fc_functionOid)
{
	HeapTuple	fc_procTup;
	HeapTuple	fc_langTup;
	Form_pg_proc fc_procStruct;
	Form_pg_language fc_langStruct;
	AclResult	fc_aclresult;

	/*
	 * 获取功能的 pg_proc 条目。对于不良 OID 抛出面向用户的错误，因为验证器可以使用用户指定的 OID 调用。
	 */
	fc_procTup = SearchSysCache1(PROCOID, ObjectIdGetDatum(fc_functionOid));
	if (!HeapTupleIsValid(fc_procTup))
		ereport(ERROR,
				(errcode(ERRCODE_UNDEFINED_FUNCTION),
				 errmsg("function with OID %u does not exist", fc_functionOid)));
	fc_procStruct = (Form_pg_proc) GETSTRUCT(fc_procTup);

	/*
	 * 获取 pg_language 条目以确定这是否是该 pg_proc 条目的正确验证函数。
	 */
	fc_langTup = SearchSysCache1(LANGOID, ObjectIdGetDatum(fc_procStruct->prolang));
	if (!HeapTupleIsValid(fc_langTup))
		elog(ERROR, "cache lookup failed for language %u", fc_procStruct->prolang);
	fc_langStruct = (Form_pg_language) GETSTRUCT(fc_langTup);

	if (fc_langStruct->lanvalidator != fc_validatorOid)
		ereport(ERROR,
				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
				 errmsg("language validation function %u called for language %u instead of %u",
						fc_validatorOid, fc_procStruct->prolang,
						fc_langStruct->lanvalidator)));

	/* 首先验证我们是否有权限使用该语言 */
	fc_aclresult = pg_language_aclcheck(fc_procStruct->prolang, GetUserId(),
									 ACL_USAGE);
	if (fc_aclresult != ACLCHECK_OK)
		aclcheck_error(fc_aclresult, OBJECT_LANGUAGE,
					   NameStr(fc_langStruct->lanname));

	/*
	 * 检查我们是否被允许执行该功能本身。如果我们可以执行它，那么编译/验证的副作用都应该是执行无法避免的。
	 */
	fc_aclresult = pg_proc_aclcheck(fc_functionOid, GetUserId(), ACL_EXECUTE);
	if (fc_aclresult != ACLCHECK_OK)
		aclcheck_error(fc_aclresult, OBJECT_FUNCTION, NameStr(fc_procStruct->proname));

	ReleaseSysCache(fc_procTup);
	ReleaseSysCache(fc_langTup);

	return true;
}
